package com.xci.sys.service;

import com.xci.core.annotation.DataScope;
import com.xci.core.annotation.Log;
import com.xci.core.annotation.Valid;
import com.xci.core.base.BaseService;
import com.xci.core.base.PrimaryKeyValue;
import com.xci.core.base.RestMessage;
import com.xci.core.domain.HistoryInfo;
import com.xci.core.exceptions.AppException;
import com.xci.core.helper.CacheHelper;
import com.xci.core.helper.Helper;
import com.xci.core.helper.TreeHelper;
import com.xci.core.internal.BatchUpdateCallback;
import com.xci.core.internal.Const;
import com.xci.core.web.TreeNode;
import com.xci.sys.dao.DeptDao;
import com.xci.sys.entity.SysDept;
import com.xci.sys.entity.SysUser;
import com.xci.sys.filter.DeptFilter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.cache.Cache;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;

/**
 * 机构服务
 */
@Service
@Slf4j
public class DeptService extends BaseService {
    @Resource
    private DeptDao deptDao;//机构数据层对象
    @Resource
    private Cache deptCache;//机构缓存对象
    @Resource
    private UserService userService;//用户服务
    @Resource
    private RoleService roleService;//角色服务

    /**
     * 新建机构
     *
     * @param entity 机构对象
     */
    @Log(module = Const.Module.Dept, msg = "新建机构")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage insert(@Valid SysDept entity) {
        return save(entity, true);
    }

    /**
     * 修改机构
     *
     * @param entity 机构对象
     */
    @Log(module = Const.Module.Dept, msg = "修改机构")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage update(@Valid SysDept entity) {
        return save(entity, false);
    }

    /**
     * 修改机构状态
     *
     * @param ids    机构主键字符串,多个逗号隔开
     * @param status 机构状态
     */
    @Log(module = Const.Module.Dept, msg = "修改机构状态")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage updateStatus(@NotBlank(message = "请指定机构主键") String ids,
                                    @NotNull(message = "请指定机构状态") Boolean status) {
        String[] idList = Helper.splitToArray(ids);
        for (String id : idList) {
            deptDao.updateStatus(id, status);
            deptCache.evict(id);
        }
        return RestMessage.success();
    }

    /**
     * 批量修改字段值
     *
     * @param keyValues 字段值集合
     */
    @Log(module = Const.Module.Dept, msg = "批量更新字段值")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage batchUpdate(List<PrimaryKeyValue> keyValues) {
        return batchUpdateParent(keyValues, new BatchUpdateCallback() {
            @Override
            public void updateParentId(String id, String parentId) {
                deptDao.updateParentId(id, parentId);
                deptCache.evict(id);
            }

            @Override
            public void updatePath(String id, String path) {
                deptDao.updatePath(id, path);
                deptCache.evict(id);
            }
        });
    }

    /**
     * 删除机构
     *
     * @param ids 机构主键字符串
     */
    @Log(module = Const.Module.Dept, msg = "删除机构")
    @Transactional(rollbackFor = Exception.class)
    public RestMessage delete(@NotBlank(message = "请指定机构主键多个逗号隔开") String ids) {
        String[] idList = Helper.splitToArray(ids);
        for (String id : idList) {
            deleteById(id);
        }
        return RestMessage.success();
    }

    /**
     * 删除机构
     *
     * @param id 机构主键
     */
    private void deleteById(String id) {
        SysUser currentUser = getCurrentUser();
        SysDept old = deptDao.selectById(id);
        if (old == null) throw new AppException(Helper.format("无效的机构主键:[{}]", id));

        if (!currentUser.getIsAdmin()&&old.getId().equals(currentUser.getDeptId()))//管理员除外
            throw new AppException(Helper.format("机构[{}]删除失败,不允许删除当前用户所在机构,必须由上级机构账号来操作", old.getName()));

        if (deptDao.selectChildCount(id) > 0) throw new AppException(Helper.format("机构[{}]存在下级机构,不允许删除", old.getName()));

        if (roleService.existByDeptId(id)) {//检测是否被角色引用
            throw new AppException(Helper.format("角色中引用了机构[{}],不允许删除", old.getName()));
        }

        if (userService.existByDeptId(id)) {//检测是否被用户引用
            throw new AppException(Helper.format("用户中引用了机构:[{}],不允许删除", old.getName()));
        }

        deptDao.delete(id,Helper.nextIdStr());
        deptCache.evict(old.getId());
        saveDeleteHistory(HistoryInfo.builder().pk(id).before(old).build());
    }

    /**
     * 检查机构编码唯一性
     *
     * @param code 机构编码
     * @param id   机构主键
     * @return 如果存在返回true
     */
    public Boolean existByCode(@NotBlank(message = "请指定机构编码") String code, String id) {
        return deptDao.existByCode(code, id);
    }

    /**
     * 检查机构名称唯一性
     *
     * @param parentId 机构上级主键
     * @param name     机构名称
     * @param id       机构主键
     * @return 如果存在返回true
     */
    public Boolean existByName(@NotBlank(message = "请指定机构上级主键") String parentId, @NotBlank(message = "请指定机构名称") String name, String id) {
        return deptDao.existByName(parentId, name, id);
    }

    /**
     * 查询单个机构对象
     *
     * @param id 机构主键
     * @return 返回机构对象
     */
    public SysDept selectById(@NotBlank(message = "请指定机构主键") String id) {
        return CacheHelper.getCacheValue(deptCache, id, () -> deptDao.selectById(id));
    }

    /**
     * 根据编码查询单个机构对象
     *
     * @param code 机构编码
     * @return 返回机构对象
     */
    public SysDept selectByCode(@NotBlank(message = "请指定机构编码") String code) {
        return deptDao.selectByCode(code);
    }

    /**
     * 获取用户所属机构
     *
     * @param userId 用户主键
     */
    public SysDept selectByUserId(@NotBlank(message = "请指定用户主键") String userId) {
        SysUser user = userService.selectById(userId);
        return selectByUserId(user);
    }

    /**
     * 获取用户所属机构
     *
     * @param user 用户对象
     */
    public SysDept selectByUserId(SysUser user) {
        if (user == null) return null;
        SysDept dept = null;
        String deptId = user.getDeptId();
        if (Helper.isNotBlank(deptId)) {
            dept = selectById(deptId);
        }
        return dept;
    }

    /**
     * 获取指定节点的直接子节点数
     *
     * @param id 主键
     * @return 返回直接子节点数量
     */
    public Integer selectChildCount(@NotBlank(message = "请指定机构主键") String id) {
        return deptDao.selectChildCount(id);
    }

    /**
     * 查询机构列表
     *
     * @param filter 机构过滤条件
     * @return 返回机构列表
     */
    @DataScope
    public List<SysDept> selectList(DeptFilter filter) {
        return deptDao.selectList(filter);
    }

    /**
     * 刷新机构缓存
     */
    public void refreshCache() {
        deptCache.clear();
        var list = deptDao.selectList(new DeptFilter());
        for (var entity : list) {
            deptCache.put(entity.getId(), entity);
        }
        log.info("刷新系统机构缓存");
    }

    /**
     * 保存机构
     *
     * @param entity  机构对象
     * @param created 是否新建
     */
    private RestMessage save(SysDept entity, boolean created) {
        //自动添加主键
        if (created && Helper.isBlank(entity.getId())) {
            entity.setId(Helper.nextIdStr());
        }
        if (Helper.isBlank(entity.getSpell())) {
            entity.setSpell(Helper.getSpell(entity.getName()));
        }

        if (Helper.isBlank(entity.getParentId())) {//验证父节点
            entity.setParentId(Const.ROOT_NODE_ID);
        }

        if (entity.getParentId().equals(entity.getId())) {
            return RestMessage.fail("修改机构" + entity.getName() + "失败，上级机构不能是自己");
        }
        var currentUser = getCurrentUser();
        if(!created && !currentUser.getIsAdmin() && entity.getId().equals(currentUser.getDeptId())){//修改数据时,当前用户不能修改自己所在的部门,只有上级账号才能修改(管理员除外)
            return RestMessage.fail("修改机构[" + entity.getName() + "]失败，当前用户不能修改自身所在部门,只有上级机构账号才能修改");
        }

        String deptId = created ? null : entity.getId();
        //机构编码唯一性验证
        if (deptDao.existByCode(entity.getCode(), deptId)) {
            return RestMessage.fail(Helper.format("机构编码[{}]已经存在", entity.getCode()));
        }

        //机构名称唯一性验证
        if (deptDao.existByName(entity.getId(), entity.getName(), deptId)) {
            return RestMessage.fail(Helper.format("机构名称[{}]已经存在", entity.getName()));
        }

        if (created) {// 新建
            Integer childCount = deptDao.selectChildCount(entity.getParentId());
            entity.setPath(childCount);
            deptDao.insert(entity);
            deptCache.evict(entity.getId());
            saveInsertHistory(HistoryInfo.builder().pk(entity.getId()).after(deptDao.selectById(entity.getId())).build());
        } else {      //修改
            var old = deptDao.selectById(entity.getId());
            deptDao.update(entity);
            deptCache.evict(entity.getId());
            saveUpdateHistory(HistoryInfo.builder().pk(entity.getId()).before(old).after(deptDao.selectById(entity.getId())).build());
        }
        return RestMessage.success();
    }

    /**
     * 转为节点列表
     *
     * @param models 模型列表
     */
    public List<TreeNode> convertToNodeList(List<SysDept> models) {
        List<TreeNode> nodes = new ArrayList<>();
        if (ObjectUtils.isEmpty(models)) {
            return nodes;
        }
        for (var item : models) {
            boolean hasChild = models.stream().anyMatch(p -> p.getParentId().equals(item.getId()));
            TreeNode node = new TreeNode();
            node.setId(item.getId());
            node.setPid(item.getParentId());
            node.setText(item.getName());
            node.setSpell(item.getSpell());
            node.setLeaf(hasChild ? 0 : 1);
            nodes.add(node);
        }
        return nodes;
    }

}